package comms;
import hotspot.HotspotApp;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;

import logging.LogToFile;
import Vis.DevicePanel;
import Vis.Visualiser;
import bodyAngle.AnglesApp;
import bodyPosition.Position;
import bodyPosition.PositionStorage;

/**
 * Sends/receives messages to the Kinect via TCP Connection
 * 
 * Forwards messages to ServerThread for phone as necessary
 * Stores hand position 
 * 
 * @author norrielm
 *
 */
public class KinectClient implements Runnable{

	public static LogToFile log = new LogToFile("ExpPos-" + System.currentTimeMillis());
	//Sockets
	private Socket echoSocket = null;
	private PrintWriter out = null;
	private BufferedReader in = null;
    //Server
    private PhoneServer server = null;
    //Connections
    protected boolean clientAlive;
    private int phoneConnected = 0;
	//Log
	//private LogToFile log;
	private boolean doLog = false;
	//vis
	private Visualiser v;
	private DevicePanel kinectPanel = null;
	//Position
	//private Hashtable<Integer,PositionStorage> bodyPositions;
	//Apps
	protected HotspotApp hotspotApp=null;
	private AnglesApp anglesApp=null;
    
    public KinectClient(Visualiser v){
		this.v=v;
		phoneConnected = 0;
    	//log = new LogToFile("Pos-" + System.currentTimeMillis());
    	
    	//storage
    	//bodyPositions = new Hashtable<Integer,PositionStorage>();  
    	
    	setupKinectPanel();

    }
    
    public void connectToServer() throws UnknownHostException, IOException{
        echoSocket = new Socket("127.0.0.1", 55554);
        out = new PrintWriter(echoSocket.getOutputStream(), true);
        in = new BufferedReader(
        		new InputStreamReader(echoSocket.getInputStream()));
	
        clientAlive = true;
    }
	
	/* *********** CLIENT CONNECTION ************/
	
	@Override
	public void run() {
		System.out.println("[Client] Start conn...");		
		try {
			connectToServer();
        } catch (UnknownHostException e) {
            System.err.println("[Client] Don't know about host.");
            System.exit(1);
        } catch (IOException e) {
            System.err.println("[Client] Couldn't get I/O for the connection.");
            System.exit(1);
        }
        
        System.out.println("[Client] Starting loop...");
		while(clientAlive){
			try {
				receiveMessageFromKinect();
				
				//Thread.sleep(10);
			}catch (IOException e) {
			} /*catch (InterruptedException e) {
				e.printStackTrace();
			}*/
		}
	}
	
	public void closeClient() throws IOException{
		System.out.println("[Client] closing connection");
		
		forwardMessageToServer("exit");
		
		clientAlive = false;
		if(!Communicator.exiting)
			Communicator.exit(0);
		
		out.close();
		in.close();
		echoSocket.close();

		System.exit(0);
	}
	
	/* *********** SEND/RECEIVE KINECT ************/
    
    /**
     * Blocking receive 
     */
	public String receiveMessageFromKinect() throws IOException{
		String serverMessage = in.readLine().trim().toLowerCase();		
		
		handleKinectMessage(serverMessage);
		
		return serverMessage;
		
	}
	
	public void append(String s){
		kinectPanel.appendToPane(s);
	}
	
	public void handleKinectMessage(String serverMessage) throws IOException{		
		v.updateLabels();		
		KinectInterface.commands c = KinectInterface.handleKinectMessage(serverMessage);
		
		//catch whichever ones relevent to the app
		switch(c){
		case EXIT:
				append("<< ["+serverMessage+"]");
				System.out.println("[Client] Closing connection...");
				forwardMessageToServer(serverMessage);	
				closeClient();
				break;
		case ACK:
				forwardMessageToServer(serverMessage);
				break;
		/* *** */
		case TRACKING_ON:			
				forwardMessageToServer(serverMessage);
				if(anglesApp!=null)
					anglesApp.scheduleAngleUpdate();
				//doLog=true;
				
				break;
		case NEW_USER:
				forwardMessageToServer(serverMessage);
				append("<< ["+serverMessage+"]");
				String[] s = serverMessage.split(" ");
				kinectPanel.getUsers().addUserWithID(Integer.valueOf(s[s.length-1]));
				kinectPanel.updateLabels();
				break;
		case LOST_USER:
				forwardMessageToServer(serverMessage);
				append("<< ["+serverMessage+"]");
				s = serverMessage.split(" ");
				try{
					kinectPanel.getUsers().removeUserWithID(Integer.valueOf(s[s.length-1]));
					kinectPanel.updateLabels();
				}catch(ArrayIndexOutOfBoundsException e){
					System.out.println("user index out of bounds");
				}
				break;
		/* *** */
		case CLICK:
				System.out.println("<< ["+serverMessage+"]");
				append("<< ["+serverMessage+"]");
				break;
		case SELECT:
				forwardMessageToServer(serverMessage);
				System.out.println("<< ["+serverMessage+"]");
				append("<< ["+serverMessage+"]");
				break;
		case PUSH:
				forwardMessageToServer(serverMessage);
				append("<< ["+serverMessage+"]");
				break;
		case RAISE_HAND:
				System.out.println("<< ["+serverMessage+"]");
				append("<< ["+serverMessage+"]");
				break;
		case CIRCLE:
				append("<< ["+serverMessage+"]");
				break;
		case WAVE:
				System.out.println("<< ["+serverMessage+"]");
				append("<< ["+serverMessage+"]");
				break;
		case SWIPE_UP:
				forwardMessageToServer(serverMessage);
				append("<< ["+serverMessage+"]");
				break;
		case SWIPE_DOWN:
				forwardMessageToServer(serverMessage);
				append("<< ["+serverMessage+"]");
				break;
		case SWIPE_L:
				forwardMessageToServer(serverMessage);
				append("<< ["+serverMessage+"]");
				break;
		case SWIPE_R:
				forwardMessageToServer(serverMessage);
				append("<< ["+serverMessage+"]");
				break;
		/* *** */
		case POS:
				//pos update: Set in file and check for hotspot [time User i hand pos x y z r g b]			
				long currTime = System.currentTimeMillis();
				
				s = serverMessage.split(" ");
				Integer userID = new Integer(s[2]);
				/*if(kinectPanel.getUsers().getUser(userID).getPositionStorage()==null){
					bodyPositions.put(userID, new PositionStorage());
				}*/
				PositionStorage bodyPos = kinectPanel.getUsers().getUser(userID).getPositionStorage();
				//= bodyPositions.get(userID);
				String place = s[3].trim();
				if((bodyPos.getBodyPosition(place)==null)){
					bodyPos.addBodyPlace(place);
				} 
				Position p = bodyPos.getBodyPosition(place);
				
				bodyPos.setColor(Float.valueOf(s[8]),
						Float.valueOf(s[9]), 
						Float.valueOf(s[10]));
				
				//check sample rate
				 /*System.out.println("Pos: " + (currTime-p.getTime()) + ">=" + 
						 ((20/1000)*1000));
				 */
				if ((currTime-p.getTime())>=50){//20Hz
					double[] pos = new double[3];
					//convert from mm to m!
					try{
						pos[0] = Float.valueOf(s[5])*1000;
						pos[1] = Float.valueOf(s[6])*1000;
						pos[2] = Float.valueOf(s[7])*1000;
						
						log.writeWTime(pos[0] + " " + pos[1] +" " + pos[2]);
						
						p.updateAcceleration(currTime, pos);
						double[] accel = p.getAcceleration();
						if(accel!=null){
							if(doLog) {
								LogToFile log = p.getLog();
								log.writeWTime(accel[0] + " " + accel[1] +" " + accel[2]);
							}
							if (accel[0] > 0.5) {
								forwardMessageToServer("Swipe left");
								System.out.println("[Client] Left " + accel[0] + " " + accel[1] +" " + accel[2]);
							} else if (accel[0] < -0.5) {
								forwardMessageToServer("Swipe right");
								System.out.println("[Client] Right " + accel[0] + " " + accel[1] +" " + accel[2]);
							}
						}
						
						if(hotspotApp!=null)
							hotspotApp.doHotspot(place, userID);	
						
						kinectPanel.updateLabels();
					}catch(NumberFormatException e){
						//hand pos off screen
					}
				}
				break;
		case ANGLE:
				if(anglesApp!=null){
					//calc angular velocity	
					s = serverMessage.split(" ");
					String bodyPlace = s[1];
					double angle;
					try{
						angle = Double.valueOf(s[s.length-1]);
					}catch(NumberFormatException e){
						angle = -1;
					}				
					anglesApp.updateBodyAngle(bodyPlace, angle, serverMessage);
				}
				break;
		case HOTSPOT:
				//if(hotspotApp==null)
					//hotspotApp = new HotspotApp(server, bodyPos);
				//forwardMessageToServer(serverMessage);
				break;
		/* *** */
		default:
				break;
		}
	}
	
	/**
	 * Send message to the server from a string
	 * @param message
	 */
	public void sendMessageToKinect(String message){
		System.out.println("[Client] Sending: " + message);
		out.println(message);
	}
	
	/* *********** PHONE SERVER ************/
	
	/**
	 * Busy wait till server connects: Should use notify
	 *
	public void waitForServerConn(){
		while (server==null && !server.serverReady){			
		}
		return ;
	}*/
	
    public void forwardMessageToServer(String s) throws IOException{
    	if(phoneConnected>0){
	    	server.sendToAll(s);
	    	System.out.println("[Client]Forwarding to phone..." + s);
    	}
    }

	public void setServer(PhoneServer server) {
		this.server = server;

    	setupHotspotApp();
	}

	public PhoneServer getServer() {
		return server;
	}
	
	public void phoneConnected(){
		phoneConnected++;
	}
	public void phoneDisconnected(){
		doLog=false;
		phoneConnected=(phoneConnected>1)?phoneConnected--:0;
	}
	
	public int connectedDevicesCount(){
		return phoneConnected;
	}
	

	public void doLogging(boolean b){
		//if(b)
			
		doLog = b;
	}
	
	/* APP SETUP */
	public void setupAnglesApp(){
		anglesApp = new AnglesApp(this);
	}	
	public void setupHotspotApp(){
		if(hotspotApp==null)
			hotspotApp = new HotspotApp(server, kinectPanel.getUsers(), v);
	}
	public void setupKinectPanel(){
		if(kinectPanel==null){
			kinectPanel = new DevicePanel(null, "Kinect", false,true);
			v.addDevice(kinectPanel);
		}
	}
	
}
